package com.eventlopp;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;

/**
 * @author 罗俊华
 * @date 2021/8/25 - 1:35 下午
 */
@Slf4j
public class EventLoopServer {


    public static void main(String[] args) {

        // 创建一个独立的 EventLoopGroup 让其来完成 worker中耗时较长的任务，避免worker在一个任务上耗费大量时间，导致本worker上的其他读写事件等待时间过长
        DefaultEventLoopGroup longTimeEventLoopGroup = new DefaultEventLoopGroup(2);

        new ServerBootstrap()
                /**
                 * group() 方法中的第一个 NioEventLoopGroup 代表是 boss eventLoop，只负责 ServerSocketChannel 上处理 accept 事件
                 *          方法中的第二个 NioEventLoopGroup 代表时 worker，只负责 socketChannel 上的 read write 读写事件，参数上的数字代表worker数量
                 */
                .group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
                /**
                 * NioServerSocketChannel.class 的 ServerSocketChannel 只会和 group() 中的第一个 NioEventLoopGroup 中的一个线程进行绑定
                 * 及时 NioEventLoopGroup 有多个线程也是浪费掉了，默认线程就只有一个
                 * */
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {

                        /**
                         * addFirst() 可以指定由哪一个 eventLoopGroup 负责处理对应的读写操作，而不是使用 worker 本身来处理读写操作
                         * 可以让 worker 腾出时间去处理本 eventLoop 下的其他读写事件的发生
                         * 交由 eventLoopGroup 处理对应 channel 的读写事件时候，就被绑定了，eventLoopGroup 中的线程唯一的与对应的channel进行绑定
                         */
                        ch.pipeline().addFirst(longTimeEventLoopGroup,"专门处理长耗时的 eventLoopGroup",new ChannelInboundHandlerAdapter(){
                            /**
                             * netty 服务端会将已连接的客户端指定为其工作的 eventLoop 线程，
                             * 也就是说：client连接到服务器以后，同一个client发送的数据，必然是同一个线程对其进行读写
                             * */
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

                                ByteBuf buf = (ByteBuf) msg;

                                log.debug("读取到客户端发来的数据：{}",buf.toString(StandardCharsets.UTF_8));

                                /**
                                 * 把消息传递给后一个handler处理
                                 */
                                ctx.fireChannelRead(msg);
                            }
                        }).addLast("worker 自己来处理读写操作",new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

                                ByteBuf buf = (ByteBuf) msg;

                                log.debug("后续读取到消息：{}",buf.toString(StandardCharsets.UTF_8));

                                super.channelRead(ctx, msg);
                            }
                        });
                    }
                }).bind(9903);
    }


}
